/*
 * process.c
 * Author: Tarl Hahn
 * version: 1.0
 *
 */

// includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "global.h"
#include "process.h"

// defines
#define PROC_STR_LEN 100

// prototypes
int update(ProcessPtr this);
int procDestruct(ProcessPtr this);
char* procToString(ProcessPtr this, int* error);
int IOlist(ProcessPtr proc);
int UIlist(ProcessPtr proc);
int procConsumerlist(ProcessPtr proc);
 
// constructs a process with a specified type, number of steps, number of requests, and a list of requests.
ProcessPtr ProcessConstruct(int pid, int priority, int type, int steps, int requests, ProcessPtr next)
{
	ProcessPtr proc = (ProcessPtr) malloc(sizeof(Process));
	proc->requests = (int*) malloc(sizeof(int) * requests);	// an array of requests, each request (e.g. io service) is issued at a specific
	proc->pid = pid;					// process ID.
	proc->priority = priority;          // priority is used by the scheduler.  Scheduler can change priority when needed, such as when using FCFS storage scheme.
	proc->process_type = type;    		// code for process type, e.g. 0=compute, 1=i0, 2=keyboard, etc.
	proc->num_steps = steps;			// number of time steps before resetting to 0 - number of instructions
	proc->num_requests = requests;		// number of requests that will be generated during the process run
	proc->start = steps;

	if (requests > 0) 
	{ 	// the frequency at which requests are generated.
		proc->req_freq = steps / requests; 
		switch(type)
		{
		case COMPUTE:
			break;
			
		case IO:
			IOlist(proc);
			break;
		
		case UI:
			UIlist(proc);
			break;
			
		case PRODUCER:
			procConsumerlist(proc);
			break;

		case CONSUMER:
			procConsumerlist(proc);
			break;
		}
	} else
	{
		proc->req_freq = 0;
	}
	proc->next = next;					// next process pointer.
	
	// member functions					// time step.
	proc->updateProc = update; 		// update function.
	proc->destruct = procDestruct;      // destroy process
	proc->toString = procToString;		// tostring
	return proc;
}

// destruct process
int procDestruct(ProcessPtr this)
{
	if (this == (ProcessPtr) NULL) { return NULL_PTR;}
	free(this);
	return NO_ERROR;
}

int IOlist(ProcessPtr proc)
{
	if(proc->num_requests < 1) { return 0; }

	// generate list
	int i, request;
	for(i = 0; i < proc->num_requests; i++)
	{
		proc->requests[i] = (rand() % 3 + 1); // get IO request 1 - 3 NETWORK, DISK_IO, VIDEO
	}
}

int UIlist(ProcessPtr proc)
{
	if(proc->num_requests < 1) { return 0; }

	// generate list
	int i, request;
	for(i = 0; i < proc->num_requests; i++)
	{
		proc->requests[i] = rand() % 2 + 4; // get IO request 4 and 5 KEYBOARD AND MOUSE
	}
}

// fill
int procConsumerlist(ProcessPtr proc)
{
	if(proc->num_requests < 1)
	{

		return NULL_PTR;
	}
	// if only one request return DiskIO
	if(proc->num_requests == 1)
	{
		proc->requests[0] = DISK_IO;
		return NO_ERROR;
	}
	// generate list
	int i, request;
	for(i = 0; i < proc->num_requests; i+=2)
	{
		proc->requests[i] = rand() % 2 + 6; // get IO request 1 - 5
		proc->requests[i+1] = proc->requests[i] + 3;
	} 

	// if requests = i-1 fill last spot with Disk_IO
	if(proc->num_requests == i-1)
	{
		proc->requests[proc->num_requests - 1] = DISK_IO;
	}

	return NO_ERROR;
}

// update process
// returns number of next request 
int update(ProcessPtr this)
{
	// if this is null return error
	if (this == (ProcessPtr) NULL) { return NULL_PTR;}
	// if num steps is zero or less return error.
	if (this->num_steps <= 0) 
	{
		this->num_steps = this->start;
		return DEAD_PROCESS;
	}
	// if num requests is greater than zero check if request is to be made
	if (this->num_requests > 0 ) 
	{   // if num requests and steps divide evenly request is made
		if(this->req_freq > 0 && this->num_steps % this->req_freq == 0)
		{
			// update num_requests 
			this->num_requests = this->num_requests - 1;
		
			// update num of steps and return
			this->num_steps = this->num_steps - 1;
			return this->requests[this->num_requests];
		}
		// update num of steps and return
		this->num_steps = this->num_steps - 1;
		return CONTINUE_PROCESS;
		
		
	} else 
	{
		// update num of steps and return
		this->num_steps = this->num_steps - 1;
		return CONTINUE_PROCESS;
	}
}

char* procToString(ProcessPtr this, int* error)
{
	// if this is null return error
	if (this == (ProcessPtr) NULL) 
	{ 
		*error = NULL_PTR;
		return 0;
	}
	
	// allocate space for string
	char* string = (char*) malloc(sizeof(char) * PROC_STR_LEN);
	// make string
	snprintf(string, PROC_STR_LEN, "Process\nProc ID: %d\nPriority: %d\nSteps Left: %d\n", 
			 this->pid, this->priority, this->num_steps);
	// trim and return string.
	return strtok (string,"");
}

/*// process test main
int main(void)
{
	int error = 0;
	int requests[] = {NETWORK};
	int request_type = 0;
	ProcessPtr proc = ProcessConstruct(0, 0, 1, 2, 1, &requests[0], NULL);
	printf(proc->toString(proc, &error));
	request_type = proc->updateProc(proc);
	printf("Request type: %d\n", request_type);
	printf(proc->toString(proc, &error));
}*/
